//gcc -fdiagnostics-color=always -g /home/hqyj/day2.c -o /home/hqyj/day2 -pthread

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>
#include <pthread.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>

#define PORT 6666
#define BUFFER_SIZE 512
#define MAX_RETRIES 3
#define RETRY_INTERVAL 1
#define USER_DB_FILE "userdb.txt"

typedef struct {
    float temperature;
    time_t temp_timestamp;
    int temp_updated;
    
    int fan_status;
    time_t fan_status_time;
    
    float threshold;
    int auto_mode;
    
    int server_running;
    pthread_mutex_t mutex;
    pthread_mutex_t userdb_mutex;
} SharedData;

typedef struct {
    struct sockaddr_in server_addr;
    struct sockaddr_in board_addr;
    socklen_t client_addr_len;
    socklen_t board_addr_len;
    int sockfd;
} NetworkInfo;

int user_exists(const char *username) {
    FILE *fp = fopen(USER_DB_FILE, "r");
    if (!fp) {
        perror("Error opening user database for reading");
        return 0;
    }
    
    char line[128];
    while (fgets(line, sizeof(line), fp)) {
        char stored_user[64];
        if (sscanf(line, "%63s", stored_user) == 1) {
            if (strcmp(stored_user, username) == 0) {
                fclose(fp);
                return 1;
            }
        }
    }
    fclose(fp);
    return 0;
}

int register_user(const char *username, const char *password) {
    FILE *fp = fopen(USER_DB_FILE, "a");
    if (!fp) {
        perror("Error opening user database for writing");
        return 0;
    }
    
    if (fprintf(fp, "%s %s\n", username, password) < 0) {
        perror("Error writing to user database");
        fclose(fp);
        return 0;
    }
    
    if (fflush(fp) != 0) {
        perror("Error flushing user database");
        fclose(fp);
        return 0;
    }
    
    fclose(fp);
    return 1;
}

int verify_user(const char *username, const char *password) {
    FILE *fp = fopen(USER_DB_FILE, "r");
    if (!fp) {
        perror("Error opening user database for verification");
        return 0;
    }
    
    char line[128];
    while (fgets(line, sizeof(line), fp)) {
        char stored_user[64], stored_pass[64];
        if (sscanf(line, "%63s %63s", stored_user, stored_pass) == 2) {
            if (strcmp(stored_user, username) == 0 && 
                strcmp(stored_pass, password) == 0) {
                fclose(fp);
                return 1;
            }
        }
    }
    fclose(fp);
    return 0;
}

void init_board_address(NetworkInfo *net, const char* ip, int port) {
    memset(&net->board_addr, 0, sizeof(net->board_addr));
    net->board_addr.sin_family = AF_INET;
    net->board_addr.sin_port = htons(port);
    net->board_addr.sin_addr.s_addr = inet_addr(ip);
    printf("Board address: %s:%d\n", ip, port);
}

int request_board_data(NetworkInfo *net, SharedData *data) {
    const char* request = "requestData";
    
    for (int i = 0; i < MAX_RETRIES; i++) {
        if (sendto(net->sockfd, request, strlen(request), 0,
                  (struct sockaddr *)&net->board_addr, net->board_addr_len) == -1) {
            perror("Send to board failed");
            sleep(RETRY_INTERVAL);
            continue;
        }
        
        printf("Board data request (%d/%d)\n", i+1, MAX_RETRIES);
        sleep(RETRY_INTERVAL);
        
        pthread_mutex_lock(&data->mutex);
        if (data->temp_updated) {
            data->temp_updated = 0;
            pthread_mutex_unlock(&data->mutex);
            return 1;
        }
        pthread_mutex_unlock(&data->mutex);
    }
    
    return 0;
}

void send_fan_command(NetworkInfo *net, const char *command, SharedData *data) {
    pthread_mutex_lock(&data->mutex);
    
    int need_update = 0;
    if (strcmp(command, "fan-on") == 0 && !data->fan_status) {
        data->fan_status = 1;
        data->fan_status_time = time(NULL);
        need_update = 1;
    } else if (strcmp(command, "fan-off") == 0 && data->fan_status) {
        data->fan_status = 0;
        data->fan_status_time = time(NULL);
        need_update = 1;
    }
    
    pthread_mutex_unlock(&data->mutex);
    
    if (!need_update) {
        printf("Fan already in requested state\n");
        return;
    }
    
    if (sendto(net->sockfd, command, strlen(command), 0,
              (struct sockaddr *)&net->board_addr, net->board_addr_len) == -1) {
        perror("Send fan command failed");
    } else {
        printf("Sent to board: %s\n", command);
    }
}

void auto_fan_control(NetworkInfo *net, float temperature, SharedData *data) {
    pthread_mutex_lock(&data->mutex);
    int current_auto = data->auto_mode;
    float current_threshold = data->threshold;
    int current_fan_status = data->fan_status;
    pthread_mutex_unlock(&data->mutex);
    
    if (!current_auto) return;
    
    if (temperature > current_threshold && !current_fan_status) {
        printf("AUTO: Temp %.2f > %.2f, turning fan ON\n", temperature, current_threshold);
        send_fan_command(net, "fan-on", data);
    } else if (temperature <= current_threshold && current_fan_status) {
        printf("AUTO: Temp %.2f <= %.2f, turning fan OFF\n", temperature, current_threshold);
        send_fan_command(net, "fan-off", data);
    }
}

void process_command(NetworkInfo *net, const char* command, char* response, 
                     size_t response_size, SharedData *data, 
                     const struct sockaddr_in* client_addr, socklen_t client_addr_len) {
    memset(response, 0, response_size);
    
    if (strncmp(command, "L ", 2) == 0) {
        char username[64], password[64];
        if (sscanf(command + 2, "%63s %63s", username, password) == 2) {
            pthread_mutex_lock(&data->userdb_mutex);
            if (verify_user(username, password)) {
                strncpy(response, "LOK", response_size);
            } else {
                strncpy(response, "LERR", response_size);
            }
            pthread_mutex_unlock(&data->userdb_mutex);
        } else {
            strncpy(response, "LERR Invalid format", response_size);
        }
        return;
    }
    
    if (strncmp(command, "R ", 2) == 0) {
        char username[64], password[64];
        if (sscanf(command + 2, "%63s %63s", username, password) == 2) {
            pthread_mutex_lock(&data->userdb_mutex);
            if (user_exists(username)) {
                strncpy(response, "RERR User exists", response_size);
            } else if (register_user(username, password)) {
                strncpy(response, "ROK", response_size);
            } else {
                strncpy(response, "RERR Database error", response_size);
            }
            pthread_mutex_unlock(&data->userdb_mutex);
        } else {
            strncpy(response, "RERR Invalid format", response_size);
        }
        return;
    }
    
    if (strcmp(command, "getTp") == 0) {
        if (request_board_data(net, data)) {
            pthread_mutex_lock(&data->mutex);
            snprintf(response, response_size, "Threshold: %.2f, Temperature: %.2f", 
                     data->threshold, data->temperature);
            pthread_mutex_unlock(&data->mutex);
        } else {
            strncpy(response, "Failed to get data from board", response_size);
        }
    }
    else if (strncmp(command, "setThreshold:", 13) == 0) {
        float new_threshold = atof(command + 13);
        pthread_mutex_lock(&data->mutex);
        data->threshold = new_threshold;
        pthread_mutex_unlock(&data->mutex);
        snprintf(response, response_size, "Threshold set to %.2f", new_threshold);
    }
    else if (strcmp(command, "open") == 0) {
        send_fan_command(net, "fan-on", data);
        strncpy(response, "Fan ON command sent", response_size);
    }
    else if (strcmp(command, "close") == 0) {
        send_fan_command(net, "fan-off", data);
        strncpy(response, "Fan OFF command sent", response_size);
    }
    else if (strncmp(command, "devput:", 7) == 0) {
        float param = strtof(command + 7, NULL);
        
        pthread_mutex_lock(&data->mutex);
        data->temperature = param;
        data->temp_timestamp = time(NULL);
        data->temp_updated = 1;
        pthread_mutex_unlock(&data->mutex);
        
        auto_fan_control(net, param, data);
        // 不再发送响应给开发板
        response[0] = '\0'; // 空响应
    }
    else if (strcmp(command, "auto-on") == 0) {
        pthread_mutex_lock(&data->mutex);
        data->auto_mode = 1;
        pthread_mutex_unlock(&data->mutex);
        strncpy(response, "Auto mode ON", response_size);
    }
    else if (strcmp(command, "auto-off") == 0) {
        pthread_mutex_lock(&data->mutex);
        data->auto_mode = 0;
        pthread_mutex_unlock(&data->mutex);
        strncpy(response, "Auto mode OFF", response_size);
    }
    else if (strcmp(command, "status") == 0) {
        pthread_mutex_lock(&data->mutex);
        snprintf(response, response_size, "Mode: %s | Fan: %s | Threshold: %.2f | Temp: %.2f",
                data->auto_mode ? "AUTO" : "MANUAL",
                data->fan_status ? "ON" : "OFF",
                data->threshold,
                data->temperature);
        pthread_mutex_unlock(&data->mutex);
    }
    else if (strcmp(command, "exit") == 0) {
        strncpy(response, "Server shutting down", response_size);
        data->server_running = 0;
    }
    else {
        strncpy(response, "Unknown command", response_size);
    }
}

void handle_board_message(NetworkInfo *net, const char* buffer, SharedData *data) {
    if (strcmp(buffer, "fan-on") == 0) {
        pthread_mutex_lock(&data->mutex);
        data->fan_status = 1;
        data->fan_status_time = time(NULL);
        pthread_mutex_unlock(&data->mutex);
        printf("Fan turned ON by board\n");
    }
    else if (strcmp(buffer, "fan-off") == 0) {
        pthread_mutex_lock(&data->mutex);
        data->fan_status = 0;
        data->fan_status_time = time(NULL);
        pthread_mutex_unlock(&data->mutex);
        printf("Fan turned OFF by board\n");
    }
    else if (strncmp(buffer, "devput:", 7) == 0) {
        float temp_value = strtof(buffer + 7, NULL);
        
        pthread_mutex_lock(&data->mutex);
        data->temperature = temp_value;
        data->temp_timestamp = time(NULL);
        data->temp_updated = 1;
        pthread_mutex_unlock(&data->mutex);
        
        printf("Board temp update: %.2f\n", temp_value);
        auto_fan_control(net, temp_value, data);
    }
}

// 消息处理线程
void* message_handler_thread(void* arg) {
    NetworkInfo *net = ((void**)arg)[0];
    SharedData *data = ((void**)arg)[1];
    
    printf("Message handler thread started\n");
    char buffer[BUFFER_SIZE];
    char response[BUFFER_SIZE];
    struct sockaddr_in sender_addr;
    socklen_t sender_len = sizeof(sender_addr);
    
    while (data->server_running) {
        memset(buffer, 0, BUFFER_SIZE);
        int ret = recvfrom(net->sockfd, buffer, BUFFER_SIZE - 1, 0, 
                          (struct sockaddr *)&sender_addr, &sender_len);
        if (ret <= 0) {
            if (data->server_running) perror("Receive failed");
            continue;
        }
        
        // 调试信息：打印发送方信息
        char sender_ip[INET_ADDRSTRLEN];
        inet_ntop(AF_INET, &sender_addr.sin_addr, sender_ip, sizeof(sender_ip));
        unsigned short sender_port = ntohs(sender_addr.sin_port);
        printf("Received %d bytes from %s:%d: %s\n", ret, sender_ip, sender_port, buffer);

        // 根据发送方IP区分消息来源
        if (sender_addr.sin_addr.s_addr == net->board_addr.sin_addr.s_addr) {
            // 开发板消息
            printf("Board message: %s\n", buffer);
            handle_board_message(net, buffer, data);
        } else {
            // 客户端消息
            printf("Client command: %s\n", buffer);
            process_command(net, buffer, response, BUFFER_SIZE, data, &sender_addr, sender_len);
            
            // 只发送非空响应
            if (response[0] != '\0') {
                if (sendto(net->sockfd, response, strlen(response), 0,
                          (struct sockaddr *)&sender_addr, sender_len) == -1) {
                    perror("Send response failed");
                } else {
                    printf("Sent response to %s:%d: %s\n", sender_ip, sender_port, response);
                }
            }
        }
    }
    
    return NULL;
}

int main(void) {
    SharedData shared_data;
    memset(&shared_data, 0, sizeof(shared_data));
    shared_data.threshold = 50.0;
    shared_data.server_running = 1;
    
    if (pthread_mutex_init(&shared_data.mutex, NULL) != 0) {
        perror("Mutex init failed");
        exit(EXIT_FAILURE);
    }
    
    if (pthread_mutex_init(&shared_data.userdb_mutex, NULL) != 0) {
        perror("UserDB mutex init failed");
        exit(EXIT_FAILURE);
    }
    
    FILE *db_file = fopen(USER_DB_FILE, "a");
    if (db_file) {
        fclose(db_file);
        printf("User database file created/opened: %s\n", USER_DB_FILE);
    } else {
        perror("Failed to create/open user database file");
        exit(EXIT_FAILURE);
    }
    
    if (access(USER_DB_FILE, W_OK) == -1) {
        perror("User database file is not writable");
        exit(EXIT_FAILURE);
    }
    
    NetworkInfo network;
    memset(&network, 0, sizeof(network));
    network.client_addr_len = sizeof(struct sockaddr_in);
    network.board_addr_len = sizeof(struct sockaddr_in);
    
    network.sockfd = socket(AF_INET, SOCK_DGRAM, 0);
    if (network.sockfd == -1) {
        perror("Socket creation failed");
        pthread_mutex_destroy(&shared_data.mutex);
        pthread_mutex_destroy(&shared_data.userdb_mutex);
        exit(EXIT_FAILURE);
    }
    
    memset(&network.server_addr, 0, sizeof(network.server_addr));
    network.server_addr.sin_family = AF_INET;
    network.server_addr.sin_port = htons(PORT);
    network.server_addr.sin_addr.s_addr = INADDR_ANY;
    
    if (bind(network.sockfd, (struct sockaddr *)&network.server_addr, 
             sizeof(network.server_addr)) == -1) {
        perror("Bind failed");
        close(network.sockfd);
        pthread_mutex_destroy(&shared_data.mutex);
        pthread_mutex_destroy(&shared_data.userdb_mutex);
        exit(EXIT_FAILURE);
    }
    
    init_board_address(&network, "192.168.54.61", 5555);
    
    printf("Server initialized on port %d\n", PORT);
    printf("Default threshold: %.2f\n", shared_data.threshold);
    printf("User database: %s\n", USER_DB_FILE);
    printf("Commands: L username password, R username password, getTp, setThreshold:value, open, close, auto-on, auto-off, status, exit\n");
    
    pthread_t thread;
    void* thread_args[2] = {&network, &shared_data};
    
    // 创建消息处理线程
    if (pthread_create(&thread, NULL, message_handler_thread, thread_args) != 0) {
        perror("Thread creation failed");
        close(network.sockfd);
        pthread_mutex_destroy(&shared_data.mutex);
        pthread_mutex_destroy(&shared_data.userdb_mutex);
        exit(EXIT_FAILURE);
    }
    
    pthread_join(thread, NULL);
    
    pthread_mutex_destroy(&shared_data.mutex);
    pthread_mutex_destroy(&shared_data.userdb_mutex);
    close(network.sockfd);
    printf("Server shutdown\n");
    return 0;
}
